﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BlockeyEssentials.ScreenSystem
{
    public abstract class ScreenManager
    {
        public ScreenManager(Game oGame)
        {
            _Game = oGame;
            _AddScreens = new List<Screen>();
            _ActiveScreens = new List<Screen>();
            _RemoveScreens = new List<Screen>();
            _SpriteBatch = new SpriteBatch(All.Graphics.GraphicsDevice);
        }

        #region Methods

        public virtual void Update()
        {
            //// -- Adds Screens -- ////
            if (ScreensToAdd.Count > 0)
            {
                // Allow Removal of Screens before Addition
                if (ScreensToRemove.Count == 0)
                {
                    var oRemove = new List<Screen>();

                    // Increases transition of all added screens
                    foreach (Screen oScreen in ScreensToAdd)
                    {
                        oScreen.Transition += oScreen.TransitionRate;
                        if (oScreen.Transition >= 1)
                        {
                            oScreen.Transition = 1.0f;
                            oRemove.Add(oScreen);
                        }
                    }

                    // Removes from Added List if completely transitioned
                    // Allows an initial action when completely added
                    foreach (Screen oScreen in oRemove)
                    {
                        oScreen.ActionWhenFullyAdded();
                        ScreensToAdd.Remove(oScreen);
                        ScreensActive.Add(oScreen);
                    }
                    oRemove.Clear();
                }
            }

            //// -- Removes Screens -- ////
            if (ScreensToRemove.Count > 0)
            {
                var oRemove = new List<Screen>();

                // Decreases Transition of Screens before Removal
                foreach (Screen oScreen in ScreensToRemove)
                {
                    oScreen.Transition -= oScreen.TransitionRate;
                    if (oScreen.Transition <= 0)
                    {
                        oRemove.Add(oScreen);
                    }
                }

                // Removes Screen forever
                foreach (Screen oScreen in oRemove)
                {
                    oScreen.ActionWhenFullyRemoved();
                    ScreensToRemove.Remove(oScreen);
                }
                oRemove.Clear();

                if (ScreensToRemove.Count == 0)
                {
                    if (ScreensActive.Count == 0)
                    {
                        if (ScreensToAdd.Count == 0)
                        {
                            All.CurrentScreenManager = All.DestinationScreenManager;
                            All.DestinationScreenManager = null;
                        }
                    }
                }
            }

            //// -- Updates Screens -- ////
            var oActiveScreens = ScreensActive.ToArray();

            foreach (Screen oScreen in oActiveScreens)
            {                
                if (oScreen.Enabled)
                    oScreen.Update();
            }
        }

        public virtual void Draw()
        {
            _SpriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);

            //// -- Draws Screens -- ////
            foreach (Screen oScreen in ScreensToAdd)
            {
                if (oScreen.Visible)
                    oScreen.Draw(_SpriteBatch);
            }
            foreach (Screen oScreen in ScreensActive)
            {
                if (oScreen.Visible)
                    oScreen.Draw(_SpriteBatch);
            }
            foreach (Screen oScreen in ScreensToRemove)
            {
                if (oScreen.Visible)
                    oScreen.Draw(_SpriteBatch);
            }

            _SpriteBatch.End();
        }

        public virtual void AddScreen(Screen oScreen)
        {
            oScreen.Transition = MathHelper.Clamp(oScreen.Transition, 0, 1);
            ScreensToAdd.Add(oScreen);
        }

        public virtual void RemoveScreen(Screen oScreen)
        {
            if (ScreensActive.Contains(oScreen))
            {
                ScreensActive.Remove(oScreen);
                oScreen.Transition = MathHelper.Clamp(oScreen.Transition, 0, 1);
                ScreensToRemove.Add(oScreen);
            }
        }

        public virtual void AddScreenImmediately(Screen oScreen)
        {
            oScreen.Transition = 1.0f;
            ScreensActive.Add(oScreen);
        }

        public virtual void RemoveScreenImmediately(Screen oScreen)
        {
            if (ScreensToAdd.Contains(oScreen))
                ScreensToAdd.Remove(oScreen);

            if (ScreensActive.Contains(oScreen))
                ScreensActive.Remove(oScreen);
            
            if (ScreensToRemove.Contains(oScreen))
                ScreensToRemove.Remove(oScreen);
        }

        public void RemoveAllScreens()
        {
            var RemovedScreens = new List<Screen>();

            foreach (Screen oScreen in ScreensToAdd)
                RemovedScreens.Add(oScreen);

            foreach (Screen oScreen in ScreensActive)
                RemovedScreens.Add(oScreen);

            foreach (Screen oScreen in RemovedScreens)
            {
                if (ScreensToAdd.Contains(oScreen))
                    ScreensToAdd.Remove(oScreen);

                if (ScreensActive.Contains(oScreen))
                    ScreensActive.Remove(oScreen);

                ScreensToRemove.Add(oScreen);
            }
        }

        public void ClearScreens()
        {
            ScreensToAdd.Clear();
            ScreensActive.Clear();
            ScreensToRemove.Clear();
        }

        #endregion

        #region Properties

        protected List<Screen> ScreensToAdd
        {
            get { return _AddScreens; }
        }

        protected List<Screen> ScreensActive
        {
            get { return _ActiveScreens; }
        }

        protected List<Screen> ScreensToRemove
        {
            get { return _RemoveScreens; }
        }

        public Game Game
        {
            get { return _Game; }
        }

        #endregion

        private List<Screen> _AddScreens;
        private List<Screen> _ActiveScreens;
        private List<Screen> _RemoveScreens;
        private Game _Game;
        protected SpriteBatch _SpriteBatch;

    }
}
